enum ElementTypes {
    H1 = 'h1',
    H2 = 'h2',
    H3 = 'h3',
    H4 = 'h4',
    H5 = 'h5',
    H6 = 'h6',
    UL = 'ul',
    OL = 'ol',
    DIV = 'div',
    LI = 'li',
    P = 'p',
    Quota = 'p',
    CODE = 'code',
}

enum DefaultConfig {
    DefaultElementType = ElementTypes.P,
    DefaultContent = `<br>`,
}

enum ClassName {
    O_DN = 'o-dn', // 不显示
    O_Quota = 'o-quota',  // 引用
    O_H1 = 'o-h1',
    O_H2 = 'o-h2',
    O_H3 = 'o-h3',
    O_H4 = 'o-h4',
    O_H5 = 'o-h5',
    O_H6 = 'o-h6',
    O_UL = 'o-ul',
    O_OL = 'o-ol',
    O_DIV = 'o-div',
    O_LI = 'o-li',
    O_P = 'o-p'
}

enum ImportID {
    Container = 'container',
    Message = 'message',
}

enum RedirectKey {
    UP = 'ArrowUp',
    PAGEUP = 'PageUp',
    PAGEDOWN = 'PageDown',
    DOWN = 'ArrowDown',
    LEFT = 'ArrowLeft',
    RIGHT = 'ArrowRight',
    BACKSPACE = 'Backspace',
    ENTER = 'Enter',
}

class ElementFactory {
    container: HTMLElement = null;
    element: HTMLElement = null;
    type: ElementTypes = null;
    content: string = null;
    _uid: number = -1;
    className: ClassName = null;
    typeList: String[] = ['h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'p', 'div', 'pre'];
    preTypeList: ElementTypes[] = [ElementTypes.UL, ElementTypes.OL]
    preTypeMap: Map<ElementTypes, ElementTypes> =
        new Map([
            [ElementTypes.OL, ElementTypes.LI],
            [ElementTypes.UL, ElementTypes.LI],
        ]);

    redirectKeyList: string[] = [
        RedirectKey.BACKSPACE, RedirectKey.DOWN, RedirectKey.UP, RedirectKey.LEFT, RedirectKey.RIGHT,
        RedirectKey.PAGEDOWN, RedirectKey.PAGEUP
    ]

    get uid() {
        return 'o' + this._uid;
    }

    getUid() {
        return 'o' + ++this._uid;
    }

    constructor(type: ElementTypes, content: string, className?: ClassName, element?: HTMLElement, uid?: string) {
        if (element) {
            this.element = element;
            this._uid = Number(uid);
            this.type = <ElementTypes>this.element.localName;
            this.content = this.element.innerText;
        } else {
            this.beforeCreateElement(type, content, className)
            this.createElement();
            this.afterCreateElement();
        }
    };

    static newInstance(type: ElementTypes, content: string, className?: ClassName, element?: HTMLElement, uid?: string) {
        return new ElementFactory(type, content, className, element, uid)
    };

    createElement() {
        if (this.typeList.includes(this.type)) {
            this.element = document.createElement(this.type);
            this.setInnerSpanHtml()
        } else {
            this.element = document.createElement('div');
            this.setInnerTypeSpanHtml();
        }
    }

    beforeCreateElement(type?: ElementTypes, content?: string, className?: ClassName) {
        this.type = type ? type : this.type;
        this.content = content ? content : this.content;
        this.className = className ? className : this.className;
    }

    afterCreateElement() {
        this.element.id = this.getUid()
        this.element.contentEditable = 'true';
        this.className && (this.element.className = this.className);
    }

    initAfterEnterCreateElement(newElement: HTMLElement, content?: string, className?: ClassName) {
        this.initAfterRedirectElement(newElement, content, className)
        this.afterCreateElement();
    }

    initAfterRedirectElement(newElement: HTMLElement, content?: string, className?: ClassName) {
        this.type = ElementTypes[newElement.nodeName];
        this.element = newElement;
        this.content = content ? content : null;
    }

    setOuterHtml(type?: ElementTypes, content?: string, className?: ClassName) {
        // 设置样式
        if (className) this.element.className = className;
        className = null; // 必须清除内部样式
        this.element.outerHTML = this.element.outerHTML.toString()
            .replace(new RegExp(`^<${this.type}`), (match, pos, origins) => {
                return '<' + (pos === 0 ? type : match);
            }).replace(new RegExp(`${this.type}>$`), (match, pos, origins) => {
                return (pos === (origins.length - match.length) ? type : match) + '>';
            });
        this.beforeCreateElement(type, content, className);
    }

    setInnerSpanHtml(content?: string) {
        this.element.innerHTML = `<span>${content ? content : this.content}</span>`
    }

    setInnerTypeSpanHtml(type?: ElementTypes, content?: string) {
        type = type ? type : this.type;
        content = content ? content : this.content;
        // 单独给 type一个id
        this.element.innerHTML = `<${type} id="${this.getUid()}"><span>${content}</span></${type}>`;
    }

    setInnerTypeSubSpanHtml(type?: ElementTypes, sub?: ElementTypes, content?: string) {
        type = type ? type : this.type;
        content = content ? content : this.content;
        // 单独给 type一个id
        this.element.innerHTML = `<${type} id="${this.getUid()}">
                                    <${sub} id="${this.getUid()}" >
                                        <span>${content}</span>
                                    </${sub}>
                                  </${type}>`;
    }

    setInnerTypeDefaultHtml(type?: ElementTypes, content?: string) {
        type = type ? type : this.type;
        content = content ? content : this.content;
        // 单独给 type一个id
        this.element.innerHTML = `<${type} id="${this.getUid()}"><p><span>${content}</span></p></${type}>`;
    }

    attach(container: HTMLElement, beforeNode?: HTMLElement, content?: string) {
        this.container = container;
        if (!beforeNode) {
            container.appendChild(this.element);
        } else {
            container.insertBefore(this.element, beforeNode);
        }
    }
}

class ContainerSetting {
    container: HTMLElement = null;
    currentInstance: ElementFactory = null;
    parser: TextParser = null;

    readyToParse: boolean = false;
    readyParseType: ElementTypes = null;
    readyParseClassName: ClassName = null;
    readyParseLocal: number = null;
    newContent: string = null;

    isOnSubElement: boolean = false;  // 是否在次级元素上

    constructor(instance: ElementFactory, parser: TextParser) {
        this.container = instance.container;
        this.currentInstance = instance;
        this.parser = parser;
        this.initListener()
    }

    get getSelection() {
        return document.getSelection().anchorOffset - 1
    }

    /**
     * 获取当前选中位置的父节点
     */
    get currentAnchorParentElement() {
        return <HTMLElement>document.getSelection().anchorNode.parentElement
    }

    /**
     * 获取当前选中位置所在节点
     */
    get currentAnchorElement() {
        return <HTMLElement>document.getSelection().anchorNode;
    }

    /**
     * 重定向节点
     */
    get redirectCurrentElement() {
        if (this.currentAnchorParentElement.id && this.currentAnchorParentElement.id !== ImportID.Container) {
            console.log('redirect currentAnchorParentElement')
            return this.currentAnchorParentElement;
        } else if (this.currentAnchorParentElement.id === ImportID.Container) {
            console.log('redirect currentAnchorElement')
            return this.currentAnchorElement;
        } else {

            if (this.currentAnchorParentElement.tagName === 'SPAN') {
                let element = this.currentAnchorParentElement;
                while (element.tagName === 'SPAN' && element.id != ImportID.Container) {
                    element = element.parentElement;
                }
                console.log('redirect', element)
                return element;
            }
            console.log('redirect currentAnchorElement')
            return this.currentAnchorParentElement
        }
    }

    /**
     * 移动光标后 判断上一节点和当前节点是否不一样
     */
    get isChangeElement() {
        if (!(this.redirectCurrentElement).id) {
            return true
        } else {
            return this.currentInstance.element.id !== (this.redirectCurrentElement).id;
        }
    }

    initListener() {
        this.container.addEventListener('input', evInput => {
            // console.log('input')
        }, true)

        this.container.addEventListener('mouseup', evMouseup => {
            // 重定向到最新的父元素
            if (this.isChangeElement) {
                this.currentInstance.initAfterRedirectElement(this.redirectCurrentElement);
                console.log('currentInstance: ', this.redirectCurrentElement);
            }
        })


        window.addEventListener('keydown', evKeydown => {
            if (evKeydown.ctrlKey === true && (evKeydown.key === 's' || evKeydown.code === 'KeyS')) {
                // 浏览保存事件
                evKeydown.preventDefault();
                this.onSaveDOM()
                return false;
            }
        }, false)


        this.container.addEventListener('keyup', evKeyup => {
            // console.log(evKeyup)
            if (evKeyup.key === ' ' || evKeyup.code === 'Space') {
                const {readyParseType, className} = this.parser
                    .parse(this.currentInstance.element.innerText.slice(0, this.getSelection).trim());

                this.readyParseType = readyParseType;
                this.readyParseClassName = className;
                this.readyToParse = !!this.readyParseType;
                this.onSpaceEvent()
                console.log('currentInstance: ', this.currentInstance.element);
            } else if (evKeyup.key === 'Enter' || evKeyup.code === 'Enter') {
                this.onEnterEvent(evKeyup)
                console.log('currentInstance: ', this.currentInstance.element);
            } else if (this.currentInstance.redirectKeyList.includes(evKeyup.key)) {
                if (this.isChangeElement) {
                    this.currentInstance.initAfterRedirectElement(this.redirectCurrentElement);
                    console.log('currentInstance: ', this.currentInstance.element);
                }
            } else {
                console.log(evKeyup.key)
            }
            // console.log(this.currentInstance.element.id);
        }, true)

        /**
         * 重写粘贴事件
         */
        this.container.addEventListener('paste', evPaste => {
            evPaste.preventDefault();
            const paste = evPaste.clipboardData.getData('text/plain')
            const selection = window.getSelection();
            if (!selection.rangeCount) return;
            selection.deleteFromDocument();
            selection.getRangeAt(0).insertNode(document.createTextNode(paste));
            selection.collapseToEnd();
        })
    }

    onEnterEvent(evKeyup?: KeyboardEvent) {
        if (!this.readyToParse) {
            // 在次级元素之中 并且
            if (this.isOnSubElement && this.currentInstance.preTypeList.includes(<ElementTypes>this.redirectCurrentElement.localName)) {
                console.log('内部换行')
                if (!this.currentInstance.element.innerText){
                    // 如果本层级内容为空
                    this.currentInstance.initAfterEnterCreateElement(this.currentAnchorElement);
                }else {
                    this.currentInstance.initAfterEnterCreateElement(this.redirectCurrentElement);
                }
            } else {
                console.log('正常换行')
                // 重定向到最新的父元素
                this.isOnSubElement = false
                this.currentInstance.initAfterEnterCreateElement(this.redirectCurrentElement);
                // 拿到换行点到行末尾的内容
                // const content = this.currentInstance.element.innerText.slice(this.getSelection + 1, );
                // this.currentInstance.initAfterEnterCreateElement(this.redirectCurrentElement);
            }
            // 改变节点html
            if (this.currentInstance.element.localName === ElementTypes.DIV) {
                // 正常换行时，从内部节点出来会自动生成一个div,这个时候应该设置为默认
                this.changeNodeHtml();
            }
            return
        }
        console.log('解析换行')

        // 当前标签的解析
        // 等到回车事件再执行的事件
        this.newContent = this.currentInstance.element.innerText.slice(this.readyParseLocal,);  // 拿到yyy
        // 根据解析结果 改变当前节点
        this.changeNodeHtml(this.readyParseType, this.newContent, this.readyParseClassName);

        // 重定向到最新的节点（回车后）
        this.currentInstance.initAfterEnterCreateElement(this.redirectCurrentElement);
        // 给默认生成的节点设置默认值
        this.changeNodeHtml();

        this.afterSetOuterHtml();
    }

    onSpaceEvent() {
        if (!this.readyToParse) {
            return
        }
        this.readyParseLocal = this.getSelection + 1;  // 解析的位置 xxx {local}yyy 取到空格位置

        const isPreType = this.currentInstance.preTypeList.includes(this.readyParseType);  // 判断是否是预设的几种标签

        if (isPreType) {
            // console.log('解析空格')
            // console.log('readyParseType', this.readyParseType);
            // console.log(this.newContent)
            this.newContent = this.currentInstance.element.innerText.slice(this.readyParseLocal,);  // 拿到yyy
            if (this.isOnSubElement) {
                // 在次级元素中解析 新的元素
                // 重定向到最新的节点
                this.currentInstance.initAfterEnterCreateElement(this.redirectCurrentElement);
                // 根据解析结果 改变当前节点
                console.log('isOnSubElement: ', this.currentInstance.element);
                this.currentInstance.setInnerTypeSubSpanHtml(
                    this.readyParseType,
                    this.currentInstance.preTypeMap.get(this.readyParseType),
                    this.newContent.length > 0 ? this.newContent : DefaultConfig.DefaultContent
                )
                // 重定向 到类似于 li的元素之上
                this.currentInstance.element = document.getElementById(this.currentInstance.uid);
                this.isOnSubElement = true;  // 次级元素
                this.readyToParse = false;
                return;
            }
            // console.log('newContent', this.newContent.length > 0 ? this.newContent : ' ');

            this.currentInstance.setOuterHtml(this.readyParseType, null, this.readyParseClassName);
            // 重定向
            this.currentInstance.element = document.getElementById(this.currentInstance.element.id);
            // 设置 类似于 ul的内部 li元素
            this.currentInstance.setInnerTypeSpanHtml(this.currentInstance.preTypeMap.get(this.readyParseType),
                this.newContent.length > 0 ? this.newContent : DefaultConfig.DefaultContent);

            // 重定向 到类似于 li的元素之上
            this.currentInstance.element = document.getElementById(this.currentInstance.uid);

            this.isOnSubElement = true;  // 次级元素
            this.readyToParse = false;
            this.readyParseType = null;
        }
    }

    changeNodeHtml(type?: ElementTypes, content?: string, className?: ClassName,) {
        type = (!type) ? <ElementTypes><unknown>DefaultConfig.DefaultElementType : type;
        content = (!content) ? DefaultConfig.DefaultContent : content;
        className = (!className) ? null : className;

        // 重组当前节点为默认标签内容
        this.currentInstance.setOuterHtml(type, null, className);
        // 重定向
        this.currentInstance.element = document.getElementById(this.currentInstance.element.id);
        // 添加span
        this.currentInstance.setInnerSpanHtml(content);
    }

    afterSetOuterHtml() {
        this.readyToParse = false;
        this.readyParseType = null;
        this.readyParseClassName = null;
        this.readyParseLocal = null;
    }

    onSaveDOM() {
        localStorage.setItem('OMG-save-all', this.container.innerHTML.toString());
        localStorage.setItem('OMG-save-id', this.currentInstance._uid + '');
        const message = document.getElementById('message');
        message.style.display = 'block';
        message.innerHTML = '保存成功';
        setTimeout(() => {
            message.style.display = 'none';
        }, 1000)
    }

}

interface TextParser {
    text: string;

    isHeader(): boolean;

    isUL(): boolean;

    isOL(): boolean;

    isQuote(): boolean; // 是否是引用，强调

    parse(text: string): ParseResult;
}

// 定义解析结果类型
type ParseResult = {
    readyParseType: ElementTypes,
    className: ClassName,
};

class TextParserIm implements TextParser {
    text: string;

    parse(text: string): ParseResult {
        this.text = text;
        console.log('text', text)
        if (this.isHeader()) {
            console.log('header')
            return {readyParseType: ElementTypes['H' + text.length], className: ClassName['O_H' + text.length]};
        } else if (this.isUL()) {
            return {readyParseType: ElementTypes.UL, className: ClassName.O_UL};
        } else if (this.isOL()) {
            return {readyParseType: ElementTypes.OL, className: ClassName.O_OL};
        } else if (this.isQuote()) {
            return {readyParseType: ElementTypes.Quota, className: ClassName.O_Quota};
        }
        return {readyParseType: null, className: null};
    }

    isHeader(): boolean {
        return /^#{1,6}$/.test(this.text);
    }

    isUL(): boolean {
        return /^-$/.test(this.text);
    }

    isOL(): boolean {
        return /^\d.$/.test(this.text);
    }

    isQuote(): boolean {
        return /^>$/.test(this.text);
    }
}

class Main {
    constructor() {
        const container: HTMLElement = document.getElementById('container');
        container.focus();
        const item = localStorage.getItem('OMG-save-all');
        let instance: ElementFactory;
        if (item) {
            container.innerHTML = item;
            const child = container.lastElementChild
            if (child.innerHTML === '<br>') {
                child.innerHTML = `<span><br></span>`;
            }
            const uid = localStorage.getItem('OMG-save-id');
            instance = ElementFactory.newInstance(null, null, null, document.getElementById(child.id), uid);
        } else {
            instance = ElementFactory.newInstance(ElementTypes.P, '李泽江你好！', null);
        }
        instance.attach(container)
        const parser = new TextParserIm();
        const containerSetting = new ContainerSetting(instance, parser);
    }
}

const main = new Main();
